﻿using System;
using System.Globalization;
using System.Net;
using System.Net.Cache;
using System.Xml;

namespace GPX
{
    /// <summary>
    /// Even in the era of semantic web and openness, Geocaching.com does not provide a web service for 
    /// querying the community created cache locations.
    /// But the peakhole that enables Google Earth to display geocaches, could be used by other applications.
    /// </summary>
    /// <remarks>mk/2008</remarks>
    static class GeoCacheKmlReader
    {
        // ** IMPORTANT **
        // The gcUserKey is the user key provided by geocaching.com used by Google Earth to query the service.
        // Find your key by (signing up and) logging in to geocaching.com, go to "my profile" and inspect the
        // GeocachingNetworkKML.kml file you download when clicking the [+Google Earth] button.
        // Replace the key below with your personal key.
        readonly static string gcUserKey = "YOUR KEY GOES HERE";

        readonly static string urlFormat = "http://www.geocaching.com/kml/kml.aspx?key=" + gcUserKey + "&BBOX={0},{1},{2},{3}";
        readonly static RequestCachePolicy httpCachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
        const string httpMethod = "GET";
        const bool httpKeepAlive = true;
        const string httpAccept = "application/vnd.google-earth.kml+xml, application/vnd.google-earth.kmz, image/*, */*";
        const string httpUserAgent = "GoogleEarth/4.3.7284.3916(Windows;Microsoft Windows Vista (Service Pack 1);en-US;kml:2.2;client:Free;type:default)";

        static void Main(string[] args)
        {
            AsyncRequestCaches(
                -122.497790,37.730385,
                -122.380087,37.812331,
                (rd, e) => {
                    if (e == null)
                        while (rd.Read())
                            Console.WriteLine(new string(' ', rd.Depth) + rd.Name + ": " + rd.Value);
                    else
                        Console.WriteLine(e.ToString());
                });
            Console.ReadLine();            
        }

        /// <summary>
        /// Simulates a GE request and responds the .kml data provided asynchronously
        /// </summary>
        /// <param name="lon1">WGS84 Latitude 1</param>
        /// <param name="lat1">WGS84 Longitude 1</param>
        /// <param name="lon2">WGS84 Latitude 2</param>
        /// <param name="lat2">WGS84 Longitude 2</param>
        /// <param name="callback">Method that is provided with the downloaded data (or an exception)</param>
        static void AsyncRequestCaches(
            double lon1, double lat1, 
            double lon2, double lat2, 
            Action<XmlReader, Exception> callback)
        {
            var url = string.Format(CultureInfo.InvariantCulture, urlFormat, lon1, lat1, lon2, lat2);
            var req = (HttpWebRequest)HttpWebRequest.Create(url);
            req.Method = httpMethod;
            req.CachePolicy = httpCachePolicy;
            req.KeepAlive = httpKeepAlive;
            req.Accept = httpAccept;
            req.UserAgent = httpUserAgent;
            var result = req.BeginGetResponse(
                asyncResult =>
                {
                    try
                    {
                        using (var res = (HttpWebResponse)req.EndGetResponse(asyncResult))
                        {
                            if (res.StatusCode == HttpStatusCode.OK)
                            {
                                using (var stream = res.GetResponseStream())
                                {
                                    using (XmlReader rd = XmlReader.Create(stream))
                                    {
                                        callback(rd, null);
                                    }
                                }
                            }
                            else
                                throw new InvalidOperationException(res.StatusDescription);
                        }
                    }
                    catch (Exception e)
                    {
                        callback(null, e);
                    }
                }, null);        
        }
    }
}
